#include "euler_operators.h"
#include "loop.h"
#include <iostream>

EulerOperators::EulerOperators(){
    idSolid = 0;
    idFace = 0;
    idLoop = 0;
    idEdge = 0;
    idHalfEdge = 0;
    idVertex = 0;
}

EulerOperators::~EulerOperators(){
}

Solid *EulerOperators::mvfs(Point _p){
    idFace = 0;
    idLoop = 0;
    idEdge = 0;
    idHalfEdge = 0;
    idVertex = 0;

    Solid *newSolid = new Solid(idSolid++);

    Loop *newLoop = new Loop(idLoop++);

    Face *newFace = new Face(idFace++, newSolid, newLoop);
    newSolid->addFace(newFace);

    Vertex *newVertex = new Vertex(idVertex++, _p);
    newSolid->addVertix(newVertex);

    HalfEdge *he = new HalfEdge(idHalfEdge++, newVertex, newLoop);

    newLoop->setFace(newFace);
    newLoop->setHERing(he);
    newVertex->setHE(he);
    he->setNext(he);
    he->setPrev(he);

    return newSolid;
}

void EulerOperators::lmev(Solid *_s, HalfEdge *_he1, HalfEdge *_he2, Point _p){
    Vertex *newVertex = new Vertex(idVertex++, _p);
    _s->addVertix(newVertex);

    Edge *newEdge = new Edge(idEdge++);
    _s->addEdge(newEdge);

    HalfEdge *he;

    he = _he1;
    while ( he != _he2 ){
        he->setStart(newVertex);
        he = he->getTwin()->getNext();
    }

    addHE(newEdge, _he2->getStart(), _he1, false);
    addHE(newEdge, newVertex, _he2, true);

    newVertex->setHE(_he2->getPrev());
    _he2->getStart()->setHE(_he2);
}

void EulerOperators::mev(Solid *_s, Face *_f1, Face *_f2, Vertex *_v1, Vertex *_v2, Vertex *_v3, Point _p){
    HalfEdge *he1, *he2;

    if( _s == NULL ) {
        std::cout << "[MEV] Solido inexistente" << std::endl;
        return;
    }
    if( _f1 == NULL ) {
        std::cout << "[MEV] Face 1 inexistente" << std::endl;
        return;
    }
    if( _f2 == NULL ) {
        std::cout << "[MEV] Face 2 inexistente" << std::endl;
        return;
    }
    if( (he1 = findHE(_f1, _v1, _v2)) == NULL ) {
        std::cout << "[MEV] Edge 1 inexistente" << std::endl;
        return;
    }
    if( (he2 = findHE(_f2, _v1, _v3)) == NULL ) {
        std::cout << "[MEV] Edge 2 inexistente" << std::endl;
        return;
    }

    lmev(_s, he1 , he2, _p);
}

void EulerOperators::mev(Solid *_s, Face *_f1, Vertex *_v1, Vertex *_v2, Point _p){
    HalfEdge *he1;

    if( _s == NULL ) {
        std::cout << "[MEV] Solido inexistente" << std::endl;
        return;
    }
    if( _f1 == NULL ) {
        std::cout << "[MEV] Face 1 inexistente" << std::endl;
        return;
    }
    if( (he1 = findHE(_f1, _v1, _v2)) == NULL ) {
        std::cout << "[MEV] Edge 1 inexistente" << std::endl;
        return;
    }

    lmev(_s, he1 , he1, _p);
}

void EulerOperators::lmef(Solid *_s, HalfEdge *he1, HalfEdge *he2){
    Loop *newLoop = new Loop(idLoop++);

    Face *newFace = new Face(idFace++, _s, newLoop);
    _s->addFace(newFace);

    Edge *newEdge = new Edge(idEdge++);
    _s->addEdge(newEdge);

    newLoop->setFace(newFace);

    HalfEdge *he, *nhe1, *nhe2;

    he = he1;
    while ( he != he2 ){
        he->setLoop(newLoop);
        he = he->getNext();
    }

    nhe1 = addHE(newEdge, he2->getStart(), he1, false);
    nhe2 = addHE(newEdge, he1->getStart(), he2, true);

    nhe1->getPrev()->setNext(nhe2);
    nhe2->getPrev()->setNext(nhe1);
    he = nhe1->getPrev();
    nhe1->setPrev(nhe2->getPrev());
    nhe2->setPrev(he);

    newLoop->setHERing(nhe1);
    he2->getLoop()->setHERing(nhe2);
}

void EulerOperators::mef(Solid *_s, Face *_f, Vertex *_v1, Vertex *_v2){
    HalfEdge *he1, *he2;

    if( _s == NULL ) {
        std::cout << "[MEF] Solido inexistente" << std::endl;
        return;
    }
    if( _f == NULL ) {
        std::cout << "[MEF] Face 1 inexistente" << std::endl;
        return;
    }
    if( (he1 = findHE(_f, _v1, NULL)) == NULL ){
         std::cout << "[MEF] Vertex 1 inexistente" << std::endl;
         return;
    }
    he2 = he1;
    while ( he2->getStart() != _v2 ){
        he2 = he2->getNext();
        if (he2 == he1){
             std::cout << "[MEF] Vertex 2 inexistente" << std::endl;
             return;
        }
    }

    lmef(_s, he1 , he2);
}

void EulerOperators::sweep(Face *_f, Point _p){
    Solid *s = _f->getSolid();
    idFace = s->sizeFaces();
    idEdge = s->sizeEdges();
    idVertex = s->sizeVertices();
    idHalfEdge = s->getEdge(idEdge - 1)->getHE1()->getID() + 1;
    idLoop = s->getFace(idFace - 1)->getLoop()->getID() + 1;

    HalfEdge *first, *scan;

    first = _f->getLoop()->getHERing();
    scan = first->getNext();

    lmev(s, scan, scan, scan->getStart()->getPoint() + _p);

    while(scan != first){
        lmev(s, scan->getNext(), scan->getNext(), scan->getNext()->getStart()->getPoint() + _p);
        lmef(s, scan->getPrev(), scan->getNext()->getNext());
        scan = scan->getNext()->getTwin()->getNext();
    }
    lmef(s, scan->getPrev(), scan->getNext()->getNext());
}

HalfEdge *EulerOperators::addHE(Edge *_e, Vertex *_v, HalfEdge *_he, bool _sign){
    HalfEdge *he;

    if (_he->getEdge() == NULL){
        he = _he;
    }
    else {
        he = new HalfEdge(idHalfEdge++);
        _he->getPrev()->setNext(he);
        he->setPrev(_he->getPrev());
        _he->setPrev(he);
        he->setNext(_he);
    }

    he->setEdge(_e);
    he->setStart(_v);
    he->setLoop(_he->getLoop());

    if (_sign == true)
        _e->setHE1(he);
    else
        _e->setHE2(he);

    return he;
}

HalfEdge *EulerOperators::findHE(Face *_f, Vertex *_v1, Vertex *_v2){
    Loop *l = _f->getLoop();
    HalfEdge *he = l->getHERing();

    do{
        if ((he->getStart() == _v1) && ( (he->getNext()->getStart() == _v2) || (_v2 == NULL) )){
            return he;
        }
        he = he->getNext();
    }while (he != l->getHERing());

    return NULL;

}
